/*

Gamebots UT Copyright (c) 2002, Andrew N. Marshal, Gal Kaminka
Gamebots UT 2003/2004 port Copyright (c) 2005
       Joe Manojlovich, Tim Garwood, Jessica Bayliss
Gamebots Pogamut derivation Copyright (c) 2007, Michal Bida
	- derived mainly from 2002 Marshal, Kaminka version

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software must also be in compliance with the Epic Games Inc. license for mods which states the following: "Your mods must be distributed solely for free, period. Neither you, nor any other person or party, may sell them to anyone, commercially exploit them in any way, or charge anyone for receiving or using them without prior written consent of Epic Games Inc. You may exchange them at no charge among other end-users and distribute them to others over the Internet, on magazine cover disks, or otherwise for free." Please see http://www.epicgames.com/ut2k4_eula.html for more information.

*/


//=============================================================================
// RemoteBot.
// Basedon Bot class
//=============================================================================
class RemoteBot extends Bot
	config(BotAPI);


var config bool bDebug;

//maybe link to bot's periphreal vision? UT bots need much less of an arc though
//like Pawn's PeriphrealVision, this value should be the cosine of the limits
//of visual field. (i.e. 0.707 = 45 degrees to each side, or a 90* arc.)
var config float remoteVisionLimit;

//The socket to the agent
var BotConnection myConnection;


//The three remote vars compliment the my vars right below. The only one
//that ever needs to be duplicated is RemoteEnemy and myTarget
//just need RemoteDestination || myDestination and RFocus || myFocus

//Who the remote bot is trying to shoot at. Used by the aiming code.
var Actor RemoteEnemy;
//Thing the remote bot is trying to move to.
var Actor RemoteDestination;
//Thing the remote bot is looking at.
var Actor myFocus;

//The spot the bot is shooting at
var FocusActorClass myTarget;

//If false, we will update myTarget location according our current focal point
var bool bTargetLocationLocked;

//The spot the bot is moving to
var vector myDestination;
//The spot the bot is looking at
var vector myFocalPoint;

// time that last sent a WAL message
var float lastWallHitTime;

// time that last sent a BMP message
var float lastBumpTime;




// delimeters for strings sent to clients. set to match those of myConnection
// as = identifies the start of an attribute
var string as;
// ae = attribute end
var string ae;
// ib = item break
var string ib;

var config float DeltaTime;

//var actor HitWall;

//var vector HitNormal;

//var vector HitLocation;

var vector pendingDestination;

var config bool bAutoSpawn;

var config bool bAutoTrace;

//If true we will draw trace lines (results of AutoTrace() functionsin BotConnection)
//in the game
var config bool bDrawTraceLines;

var config bool bShowFocalPoint;

var config bool bPerfectLocationAim;

//maximum number we can multiply BaseSpeed of the bot
var config float MaxSpeed;

var config string DesiredSkin;

var bool bSecondJumpPossible;



// called when using movetoward with bAdvancedTactics true to temporarily modify destination
event AlterDestination();

// Mover has notifies pawn that pawn is underneath it
function UnderLift(Mover M)
{
	// SENDMESSAGE
	// !!! possible new message
}

//override - using translocator in this function
event NotifyMissedJump()
{
	// SENDMESSAGE
	// !!! possible new message?

}

function AwardAdrenaline(float Amount)
{
	super.AwardAdrenaline(Amount);
	myConnection.SendLine("ADG"$ib$as$"Amount"$ib$Amount$ae);
}


//override - using translocator in this function
function Actor FaceActor(float StrafingModifier)
{
	return none;
}


//Take a wild guess
function StartMatch()
{
	// SENDMESSAGE
	// !!! possible new message
}


function pickup SpawnInventory( class<pickup> PickupType )
{
	local Pickup Copy;
//	local inventory Inv;

	if (Pawn == None)
		return None;

	Copy = Spawn(PickupType,Pawn,,Pawn.Location,rot(0,0,0));
	Copy.bDropped = true;
	Copy.LifeSpan = 2;
	//Copy.Instigator = Pawn;

	/*
	if (Copy.IsA('Weapon'))
	{
		for( Inv=Pawn.Inventory; Inv!=None; Inv=Inv.Inventory )
		{
			if (Copy == Inv)
				log("Found same Inventory "$Inv$ " and "$Copy);
		}

	}  */
	/*
	if (Pawn.AddInventory(Copy))
	{
		log("Inventory "$Copy$" added succes");
	}
	else
	{
		log("Inventory "$Copy$" added fail");
	}


    for( Inv=Pawn.Inventory; Inv!=None; Inv=Inv.Inventory )
	{
		log("Inventory "$i$" is "$Inv);
	}*/

	return Copy;
}

function DestroyPawn()
{
	if (Pawn == None)
		return;

	Pawn.Died(none, class'DamageType', Pawn.Location);
}

function RespawnPlayer(optional vector startLocation, optional rotator startRotation)
{
	if (IsInState('Dead') && !bAutoSpawn) //
	{
		RemoteRestartPlayer(startLocation,startRotation);
	}
	else if (bAutoSpawn)
	{
		bAutoSpawn = false; //otherwise it would get respawned in Dead state without specifyed locatin, rotation
		DestroyPawn();
		GotoState('Dead');
		RemoteRestartPlayer(startLocation,startRotation);
		bAutoSpawn = true;

	}
	else  if (!IsInState('Dead') && !bAutoSpawn)
	{
		DestroyPawn();
		GotoState('Dead');
		RemoteRestartPlayer(startLocation,startRotation);

	}
}

function RemoteRestartPlayer(optional vector startLocation, optional rotator startRotation)
{
	if( Level.Game.isA('BotDeathMatch'))
	{
		BotDeathMatch(Level.Game).RemoteRestartPlayer( self, startLocation, startRotation );
	}

	if (Pawn != None)
	{
		myConnection.sendLine("SPW");
		gotostate('StartUp','DoStop');
	}
	else
	{
		log("Pawn is None after ResartPlayer");
	}

}

function bool WeaponFireAgain(float RefireRate, bool bFinishedFire)
{

	if (!RemoteHasAmmo())
	{
		Super.StopFiring();
		return false;
	}

	return true;
}

//Use from local code - stops firing and ticks weapon to let it stop
function HaltFiring()
{
	//Pawn.Weapon.StopFire(Pawn.Weapon.BotMode);
	bFire = 0;
	bAltFire = 0;
	Super.StopFiring();
	//Pawn.Weapon.Tick(0.001);
}

//we have to override this function, otherwise US will reset our vision settings
//and etc..
function ResetSkill()
{
	//log("We are in ResetSkill");
}

//Overriding function, otherwise code from states in Bot.uc would be executed
function HearPickup(Pawn Other)
{
	//myConnection.SendLine("HRP" $ib$as$"Rotation" $ib$ rotator(Location - Other.Location) $ae);
}

//Overriding function, otherwise code from states in Bot.uc would be executed
function HearNoise (float Loudness,actor Actor )
{

}

event AIHearSound( actor Actor, int Id, sound S, vector SoundLocation, vector Parameters, bool Attenuate ) {

	local float floatDist;

 	if (Pawn == none)
		return;

   	//log("NOISE - parameters "$Parameters);

	floatDist = sqrt(square(Pawn.Location.x - Actor.Location.x) + square(Pawn.Location.y - Actor.Location.y) + square(Pawn.Location.z - Actor.Location.z));

	if (floatDist > S.BaseRadius)
		return;

	if( Actor.IsA('Pickup') )
	{
		myConnection.SendLine("HRP" $ib$as$
			"Source" $ib$ actor.Class $ae$ib$as$
			"Name" $ib$ actor.Name $ae$ib$as$
			"Rotation" $ib$ rotator(Location - actor.Location) $ae);
	}
	else
	{
		if( Actor.isA('Pawn') && Pawn(Actor).Controller != none)
		{
			myConnection.sendLine("HRN" $ib$as$
				"Source" $ib$ Pawn(Actor).Controller$Pawn(Actor).Controller.PlayerReplicationInfo.PlayerID $ae$ib$as$
				"Rotation" $ib$ rotator(Location - actor.Location) $ae);
		}
		else
		{
			myConnection.SendLine("HRN" $ib$as$
				"Source" $ib$ actor $ae$ib$as$
				"Rotation" $ib$ rotator(Location - actor.Location) $ae);
		}
	}
}

event SoakStop(string Problem)
{
	myConnection.SendLine("SOAKED" $ib$as$ "Problem" $ib$ Problem $ae);
	log("We are Soaked! Problem: "$Problem);
}

//Zone the bot is standing on has changed
singular event BaseChange()
{
	Super(Pawn).BaseChange();
	// !!! possible SENDMESSAGE
	//myConnection.SendLine("BASETEST" $ib$as$ "Actor" $ib$ HitActor $ae$ib$as$"Location" $ib$ HitLocation $ae);
}

//handles sending messages to all and to team
function RemoteBroadcast( coerce string Msg)
{
	//local Pawn P;
	local bool bGlobal;

	bGlobal = false;

	    //Set the text bubble, which will be shown on the HUDs of Human players
    //when they see the bot
    if (Pawn != none)
   	{
		GBxPawn(Pawn).SetTextBubble("", Msg, true, 5);
	}

	//@ at the beginning of the message means we want to send global message
	if ( Left(Msg, 1) ~= "@" )
	{
		Msg = Right(Msg, Len(Msg)-1);
		bGlobal = true;
	}

	if ( bGlobal || !Level.Game.bTeamGame )
	{
	    //For players
	    Level.Game.Broadcast(self, Msg, 'Say');
	    //For RemoteBots
	    SendGlobalMessage( Msg );
	    return;
	}

	if (Level.Game.bTeamGame)
	{
		//For players
		Level.Game.BroadcastTeam(self, Msg, 'TeamSay');
		//For RemoteBots
		SendTeamMessage( Msg );
		return;
	}
}

//Called right before bot falls off something
//If bot running, set bCanJump to true - let him fall
//if walking, won't fall
event MayFall()
{
	local bool bFall;

	bFall = !Pawn.bIsWalking;

	if(!bfall)
	{
		GotoState('Startup', 'Begin');
	}

	myConnection.SendLine("FAL" $ib$as$
		"Fell" $ib$ bFall $ae$ib$as$
		"Location" $ib$ Pawn.Location $ae);

	Pawn.bCanJump = bFall;
}

function float GetSpeed()
{
	if(Pawn.bIsWalking)
	{
		return Pawn.WalkingPct;
	}
	else
	{
		return 1.0;
	}
}

//can be autocalled when bot is shot by team mate
function YellAt(Pawn Moron);

function HandlePickup(Pickup Pickup)
{
	local string uniqueId;

	if (Pickup.PickUpBase == none)
		uniqueId = string(Pickup);
	else
		uniqueId = string(Pickup.PickUpBase);

		myConnection.SendLine("INV {Id " $ uniqueId $
			"} {Event Pickup" $
			"} {Amount " $ myConnection.GetItemAmount(Pickup) $
			"} {Location " $ Pickup.Location $
			"} {Reachable " $ actorReachable(Pickup) $
			"} {Class " $ Pickup.Class $
			"}");
}

// Called when get new weapon or ammunition for weapon we do not have yet.
// Called just once per weapon type or per new ammunition type (notify new object in our inventory, NOT pickup)
// For exporting weapons we have new attributes.
function NotifyAddInventory( Inventory inputInventory )
{
	local string outstring;

	Super.NotifyAddInventory(inputInventory);

	outstring = "AIN {Id " $ inputInventory $
		"} {Class " $ inputInventory.Class $
		"}";

	if (inputInventory.IsA('Weapon'))
	{
		outstring = outstring $ " {Melee " $ Weapon(inputInventory).bMeleeWeapon $
			"} {Sniping " $ Weapon(inputInventory).bSniping $
			"}";

		if (Weapon(inputInventory).AmmoClass[0] != none)
		{
			outstring = outstring $ " {PrimaryInitialAmmo " $ Weapon(inputInventory).AmmoClass[0].default.InitialAmount $
				"} {MaxPrimaryAmmo " $ Weapon(inputInventory).AmmoClass[0].default.MaxAmmo $
				"}";
        }

		if (Weapon(inputInventory).AmmoClass[1] != none)
		{
			outstring = outstring $ " {SecondaryInitialAmmo " $ Weapon(inputInventory).AmmoClass[1].default.InitialAmount $
				"} {MaxSecondaryAmmo " $ Weapon(inputInventory).AmmoClass[1].default.MaxAmmo $
				"}";
		}
	}

	myConnection.SendLine(outstring);
}

function SetHealth(int ammount)
{
	if (Pawn == none)
		return;
	if ((ammount > 0) && (ammount < 200))
		Pawn.Health = ammount;
}

function SetAdrenaline(int amount)
{
	if ((amount > 0) && (amount <= 100))
		Adrenaline = amount;
}


function BotVoiceMessage(name messagetype, byte MessageID, Controller Sender);

// !!! need functions to send arbitrary string messages

function SendVoiceMessage(PlayerReplicationInfo Sender, PlayerReplicationInfo Recipient, name messageType, byte messageID, name broadcastType)
{
	Super.SendVoiceMessage( Sender, Recipient, messageType, messageID, broadcastType );
}

//functions to send tokenized messages
/*
function SendTeamMessage(PlayerReplicationInfo Recipient, name MessageType, byte MessageID, float Wait)
{
	//log(self@"Send message"@MessageType@MessageID@"at"@Level.TimeSeconds);
	if ( (MessageType == OldMessageType) && (MessageID == OldMessageID)
		&& (Level.TimeSeconds - OldMessageTime < Wait) )
		return;

	//log("Passed filter");
	OldMessageID = MessageID;
	OldMessageType = MessageType;

	SendVoiceMessage(PlayerReplicationInfo, Recipient, MessageType, MessageID, 'TEAM');
}*/
/*
function SendGlobalMessage(PlayerReplicationInfo Recipient, name MessageType, byte MessageID, float Wait)
{
	//log(self@"Send message"@MessageType@MessageID@"at"@Level.TimeSeconds);
	if ( (MessageType == OldMessageType) && (MessageID == OldMessageID)
		&& (Level.TimeSeconds - OldMessageTime < Wait) )
		return;

	//log("Passed filter");
	OldMessageID = MessageID;
	OldMessageType = MessageType;

	SendVoiceMessage(PlayerReplicationInfo, Recipient, MessageType, MessageID, 'GLOBAL');
}*/

//events for recieving arbitrary string messages, called manually
event RemoteNotifyClientMessage( string PlayerName, coerce string S )
{
	myConnection.SendLine("VMS" $ib$as$ "Name" $ib$ PlayerName $ae$ib$as$ "String" $ib$ S $ae);
}

event RemoteNotifyTeamMessage( string PlayerName, coerce string S )
{
	myConnection.SendLine("VMT" $ib$as$ "Name" $ib$ PlayerName $ae$ib$as$ "String" $ib$ S $ae);
}

function SendTeamMessage( string S )
{
	local Controller C;

	for( C = Level.ControllerList; C != None; C = C.NextController )
	{
		if( C.PlayerReplicationInfo.Team != none )
		{
			if( C.isA('RemoteBot') && C != self && C.PlayerReplicationInfo.Team.TeamIndex == PlayerReplicationInfo.Team.TeamIndex)
			{
				RemoteBot(C).RemoteNotifyTeamMessage( PlayerReplicationInfo.PlayerName , S );
			}
		}
	}
}

function SendGlobalMessage( string S )
{
	local Controller C;

	for( C = Level.ControllerList; C != none; C = C.NextController )
	{
		if( C.isA('RemoteBot') && C != self )
		{
			RemoteBot(C).RemoteNotifyClientMessage(PlayerReplicationInfo.PlayerName, S);
		}
	}
}

//other code may try to call this. make sure nada happens
//!!! need to change - make sure orders are actually recorded somewhere
//             for reference
function SetOrders(name NewOrders, Controller OrderGiver);

//events that are called when head, feet or whole bot changes zones
//e.g. air to water or lava
//only way to tell if underwater until drowning damage starts
/*
function FootZoneChange(ZoneInfo newFootZone)
{
	Super.FootZoneChange(newFootZone);

	myConnection.SendLine("ZCF" $ib$as$ "Id" $ib$ newFootZone $ae);
}
function HeadZoneChange(ZoneInfo newHeadZone)
{
	Super.HeadZoneChange(newHeadZone);

	myConnection.SendLine("ZCH" $ib$as$ "Id" $ib$ newHeadZone $ae);
} */
event bool NotifyPhysicsVolumeChange( PhysicsVolume NewVolume )
{
	myConnection.SendLine("VCH" $ib$as$ "Id" $ib$ NewVolume $ae$ib$as$ "PainCausing" $ib$ NewVolume.bPainCausing $ae);
	return super.NotifyPhysicsVolumeChange( NewVolume );
}

function ZoneChange(ZoneInfo newZone)
{
	Super.ZoneChange(newZone);
	if (myConnection == none)
		return;
	myConnection.SendLine("ZCB" $ib$as$ "Id" $ib$ newZone $ae);
}

//Called when weapon is switched - may happen automatically
function ChangedWeapon()
{
//	local int usealt;
	local string WeaponName;

	if ( (Pawn.Weapon != none) && (Pawn.Weapon == Pawn.PendingWeapon) )
	{
		SwitchToBestWeapon();
		if ( Pawn.Weapon.GetStateName() == 'DownWeapon' )
			Pawn.Weapon.GotoState('Idle');
		Pawn.PendingWeapon = None;
	}
	else
		super.ChangedWeapon();

	if ( Pawn.Weapon != None )
	{
		if (bFire > 0)
		{
 			bAltFire = 0;
			bFire = 1;
			Pawn.Weapon.Fire(1.0);
		}
 		else if (bAltFire > 0)
 		{
			bAltFire = 0;
			bFire = 1;
			Pawn.Weapon.AltFire(1.0);
		}
		Pawn.Weapon.SetHand(0);

	}
	// !!! use or just make people get from status update
	if (Pawn.Weapon == none)
		WeaponName = "None";
	else
		WeaponName = string(Pawn.Weapon);
	myConnection.SendLine("CWP" $ib$as$ "Id" $ib$ WeaponName $ae$ib$as$
							"Class" $ib$ Pawn.Weapon.Class $ae);
}

event Touch( Actor Other )
{
	//TODO: Add a check if actor is a player or bot
	myConnection.SendLine("TCH" $ib$as$ "Id" $ib$ Other $ae$ib$as$
							"Location" $ib$ Other.Location $ae$ib$as$
							"Rotation" $ib$ Other.Rotation $ae);
}

// called from pathnodes that unitelligent creatures are supposed to avoid
function FearThisSpot(AvoidMarker aSpot);

//called on hitting a wall
event bool NotifyHitWall( vector HitNormal, actor HitWall )
{
	if ( Level.TimeSeconds - 0.5 >= lastWallHitTime )
	{
		myConnection.SendLine("WAL" $ib$as$ "Id" $ib$ HitWall $ae$ib$as$
			"Normal" $ib$ HitNormal $ae$ib$as$
			"Location" $ib$ Pawn.Location $ae);

		lastWallHitTime = Level.TimeSeconds;
	}

	return true;
}


//called on collisions with other actors
event bool NotifyBump(actor Other)
{
	local vector VelDir, OtherDir;
	local float speed, dist;
	local string ActorId;

	if ( TimerRate <= 0 )
		setTimer(1.0, false);

	speed = VSize(Velocity);
	if ( speed > 10 )
	{
		VelDir = Velocity/speed;
		VelDir.Z = 0;
		OtherDir = Other.Location - Location;
		OtherDir.Z = 0;
		OtherDir = Normal(OtherDir);
		if ( (VelDir Dot OtherDir) > 0.8 )
		{
			Velocity.X = VelDir.Y;
			Velocity.Y = -1 * VelDir.X;
			Velocity *= FMax(speed, 280);
		}
	}

	if ( Level.TimeSeconds - 0.5 >= lastBumpTime )
	{
		if (Other.isA('Pawn'))
			ActorId = Pawn(Other).Controller $ Pawn(Other).Controller.PlayerReplicationInfo.PlayerID;
		else
			ActorId = string(Other);

		myConnection.SendLine("BMP {Id " $ ActorId $
			"} {Location " $ Other.Location $
			"}");

		lastBumpTime = Level.TimeSeconds;
	}

	// Need to disable bumping ???
	//Disable('Bump');
	//TODO: Experiment with this?
	return false;
}

function LongFall()
{
	// SENDMESSAGE ???
}


//called periodicaly for each player in view
function SeePlayer(Pawn SeenPlayer)
{
	local Controller C;
	local string TeamIndex,OutString;

	if (SeenPlayer == none)
		return;

	if(Level.Game.isA('BotTeamGame'))
	{
		TeamIndex = string(SeenPlayer.PlayerReplicationInfo.Team.TeamIndex);
	}
	else
	{
    	TeamIndex = "255";
	}

	OutString = "SEE {Id " $ SeenPlayer.Owner $ SeenPlayer.PlayerReplicationInfo.PlayerID $
		"} {Rotation " $ SeenPlayer.Rotation $
		"} {Location " $ SeenPlayer.Location $
		"} {Velocity " $ SeenPlayer.Velocity $
		"} {Name " $ SeenPlayer.PlayerReplicationInfo.PlayerName $
		"} {Team " $ TeamIndex $
		"} {Reachable " $ actorReachable(SeenPlayer) $
		"} {Weapon " $ SeenPlayer.Weapon $
		"}";

	C = Controller(SeenPlayer.Owner);
	if( C.Pawn.Weapon.GetFireMode(0).IsFiring() )
		OutString = outstring $ " {Firing 1}";
	else if( C.Pawn.Weapon.GetFireMode(1).IsFiring() )
		OutString = outstring $ " {Firing 2}";
	else
		OutString = outstring $ " {Firing 0}";
	//log(outstring);
	myConnection.sendLine(OutString);
}

/* Added func RemoteKilled - should make things cleaner, called from GameTypeGame server
	25.10.2006 Michal Bida
*/
function RemoteKilled(Controller Killer, Controller Killed, class<DamageType> damageType)
{
	if (Killer == None)
		myConnection.sendLine("KIL" $ib$as$ "Id" $ib$ Killed $ Killed.PlayerReplicationInfo.PlayerID $ae$ib$as$
		"Killer" $ib$ "None" $ae$ib$as$
		"DamageType" $ib$ damageType $ae);
	else
		myConnection.sendLine("KIL" $ib$as$ "Id" $ib$ Killed $ Killed.PlayerReplicationInfo.PlayerID $ae$ib$as$
        "Killer" $ib$ Killer $ Killer.PlayerReplicationInfo.PlayerID $ae$ib$as$
        "DamageType" $ib$ damageType $ae);
}

/* Added func RemoteDied - should make things cleaner, called from GameTypeGame server
	25.10.2006 Michal Bida
*/
function RemoteDied(Controller Killer, class<DamageType> damageType)
{
	if (Killer == None)
		myConnection.SendLine("DIE" $ib$as$ "Killer" $ib$ "None" $ae$ib$as$ "DamageType" $ib$ damageType $ae);
	else
		myConnection.SendLine("DIE" $ib$as$ "Killer" $ib$ Killer $ Killer.PlayerReplicationInfo.PlayerID
				$ae$ib$as$ "DamageType" $ib$ damageType $ae);
}

/* NearWall() returns true if there is a nearby barrier at eyeheight, and
changes Focus to a suggested value
*/
//Potentially usefull
/*
function bool NearWall(float walldist)
{
	local actor HitActor;
	local vector HitLocation, HitNormal, ViewSpot, ViewDist, LookDir;

	LookDir = vector(Rotation);
	ViewSpot = Location + BaseEyeHeight * vect(0,0,1);
	ViewDist = LookDir * walldist;
	HitActor = Trace(HitLocation, HitNormal, ViewSpot + ViewDist, ViewSpot, false);
	if ( HitActor == None )
		return false;

	ViewDist = Normal(HitNormal Cross vect(0,0,1)) * walldist;
	if (FRand() < 0.5)
		ViewDist *= -1;

	if ( FastTrace(ViewSpot + ViewDist, ViewSpot) )
	{
		Focus = Location + ViewDist;
		return true;
	}

	ViewDist *= -1;

	if ( FastTrace(ViewSpot + ViewDist, ViewSpot) )
	{
		Focus = Location + ViewDist;
		return true;
	}

	Focus = Location - LookDir * 300;
	return true;
}
*/

//Do adrenaline combo
function RemoteCombo( string ComboClassName )
{
	if ((Pawn != none) && !Pawn.InCurrentCombo() && (Adrenaline == AdrenalineMax) )
		Pawn.DoComboName( ComboClassName );

}
//Jumps a bot
function RemoteJump(bool bDouble, optional float JumpZ)
{

	if (Pawn == none)
		return;

	if (Pawn.Physics != PHYS_Falling)  //TODO: Check all bad physics
	{
		//if we want to jump by exact force
	    if (JumpZ != 0)
	    {
	    	//Can't jump higher then "DoubleJump" limit
	    	if (JumpZ > 2 * xPawn(Pawn).JumpZ)
	    		JumpZ = 2 * xPawn(Pawn).JumpZ;

	    	GBxPawn(Pawn).RemoteJump(JumpZ);
	    }
	    else
	    {

			Pawn.DoDoubleJump(true);

			//if double jump we just boost are jump
			if (bDouble)
			{
				Pawn.Velocity.z += xPawn(Pawn).JumpZ;// + xPawn(Pawn).MultiJumpBoost;
			}
		}
	}

}


//Intercept FireWeapon - is called from other code
function FireWeapon();

function bool RemoteHasAmmo()
{
	if ( (Pawn == none) || (Pawn.Weapon == none) )
		return false;

	if (bFire == 1)
	{
		return (Pawn.Weapon.AmmoAmount(0) >= Pawn.Weapon.GetFireMode(0).AmmoPerFire);
	}

	if (bAltFire == 1)
	{
		return (Pawn.Weapon.AmmoAmount(1) >= Pawn.Weapon.GetFireMode(1).AmmoPerFire);
    }
}

function RemoteFireWeapon(bool bUseAltMode)
{

	if ((Pawn == none) || (Pawn.Weapon == none))
		return;

	//If we are changing from one firing mode to another we need to stopshooting
	if (bUseAltMode && (bFire == 1))
		super.StopFiring();
	else if (!bUseAltMode && (bAltFire == 1))
		super.StopFiring();


	if ( !bUseAltMode )
	{
		bFire = 1;
		bAltFire = 0;
		if (!RemoteHasAmmo())
		{
			bFire = 0;
			return;
		}
    	Pawn.Weapon.BotMode = 0;
    	Pawn.Weapon.StartFire(0);
	}
	else
	{
		bFire = 0;
		bAltFire = 1;
		if (!RemoteHasAmmo())
		{
			bAltFire = 0;
			return;
		}
    	Pawn.Weapon.BotMode = 1;
    	Pawn.Weapon.StartFire(1);
	}

}

// check for line of sight to target deltatime from now.
//used by missle launcher and others to abort firing
// may want to reimplement
function bool CheckFutureSight(float deltatime)
{
	return true;
}

/*
AdjustAim()
Returns a rotation which is the direction the bot should aim - after introducing the appropriate aiming error
*/
function rotator AdjustAim(FireProperties FiredAmmunition, vector projStart, int aimerror)
{
	local rotator FireRotation, TargetLook;
	local float FireDist, TargetDist, ProjSpeed;
	local actor HitActor;
	local vector FireSpot, FireDir, TargetVel, HitLocation, HitNormal;
	local int realYaw;
	local bool bDefendMelee, bClean, bLeadTargetNow;

	if ( FiredAmmunition.ProjectileClass != None )
		projspeed = FiredAmmunition.ProjectileClass.default.speed;
	//log("AIMError is "$aimerror);
	// make sure bot has a valid target

	// This means we want to shoot at location
	if ( Target == None )
	{
		//we will set our own FocusActor as a target
		Target = myTarget;
	}

	//if our target is our FocusActor, we want to update its position - if not locked
	//(according to bots new focal point due to rotation, movement, etc.)
	if ( (Target == myTarget) && !bTargetLocationLocked)
		myTarget.SetLocation(myFocalPoint);

	/*
	if ( Target == None )
	{
		Target = Enemy;
		if ( Target == None ) //shooting at a location
		{

			//HACK: We will set ShotTarget to ourselves, so the projectile calls
			//ReceiveProjectileWarning on us and we will distribute it to other bots
			ShotTarget = Pawn;

			//TODO: Why to do this?
			//SetRotation( Rotator( myFocalPoint ));
			FireSpot = myFocalPoint;
			TargetDist = VSize(myFocalPoint - Pawn.Location);

			if (!bPerfectLocationAim)
				aimerror = AdjustAimError(aimerror,TargetDist,false,FiredAmmunition.bInstantHit, false);

			if ( Pawn(Target) == None )
			{
				if ( !FiredAmmunition.bTossed )
				{

					FireRotation = rotator(myFocalPoint - projstart);
					realYaw = FireRotation.Yaw;
					if (!bPerfectLocationAim)
						FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
    				SetRotation(FireRotation);
    				UpdatePawnViewPitch();
					return FireRotation;
				}
				else
				{
					FireDir = AdjustToss(projspeed,ProjStart,myFocalPoint,true);
					FireRotation = Rotator(FireDir);
					realYaw = FireRotation.Yaw;
					if (!bPerfectLocationAim)
						FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);

					SetRotation(FireRotation);
					UpdatePawnViewPitch();
					return FireRotation;
				}
			}

		}
	}             */

	//TODO: We have target defined - Comment lines below out?
	//if ( Pawn(Target) != None )
		//Target = Pawn(Target).GetAimTarget();

	FireSpot = Target.Location;
	TargetDist = VSize(Target.Location - Pawn.Location);


	// Here we are aiming at stationary objects( not a location, not a pawn)
	if ( Pawn(Target) == None )
	{
		// if bPerfectLocationAim is true, it means that no aim correction will be performed on staitonary objects
		if (!bPerfectLocationAim)
			aimerror = AdjustAimError(aimerror,TargetDist,false,FiredAmmunition.bInstantHit, false);

		if ( !FiredAmmunition.bTossed )
		{

			FireRotation = rotator(Target.Location - projstart);
			realYaw = FireRotation.Yaw;
			if (!bPerfectLocationAim)
				FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
			SetRotation(FireRotation);
			UpdatePawnViewPitch();
			return FireRotation;

		}
		else
		{
			FireDir = AdjustToss(projspeed,ProjStart,Target.Location,true);

			FireRotation = Rotator(FireDir);
			realYaw = FireRotation.Yaw;
			if (!bPerfectLocationAim)
				FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);

			SetRotation(FireRotation);
			UpdatePawnViewPitch();
			return FireRotation;

		}
	}

	bLeadTargetNow = FiredAmmunition.bLeadTarget && bLeadTarget;
	bDefendMelee = ( (Target == Enemy) && DefendMelee(TargetDist) );
	aimerror = AdjustAimError(aimerror,TargetDist,bDefendMelee,FiredAmmunition.bInstantHit, bLeadTargetNow);

	// lead target with non instant hit projectiles
	if ( bLeadTargetNow )
	{
		TargetVel = Target.Velocity;

		// hack guess at projecting falling velocity of target
		if ( Target.Physics == PHYS_Falling )
		{
			if ( Target.PhysicsVolume.Gravity.Z <= Target.PhysicsVolume.Default.Gravity.Z )
				TargetVel.Z = FMin(TargetVel.Z + FMax(-400, Target.PhysicsVolume.Gravity.Z * FMin(1,TargetDist/projSpeed)),0);
			else
			{
				//TODO: Correct?
				TargetVel.Z = FMin(0, TargetVel.Z);
			}
		}

		if ( bLeadTargetNow )
		{
			// more or less lead target (with some random variation)
			FireSpot += FMin(1, 0.7 + 0.6 * FRand()) * TargetVel * TargetDist/projSpeed;
			FireSpot.Z = FMin(Target.Location.Z, FireSpot.Z);
		}
		if ( (Target.Physics != PHYS_Falling) && (FRand() < 0.55) && (VSize(FireSpot - ProjStart) > 1000) )
		{
			// don't always lead far away targets, especially if they are moving sideways with respect to the bot
			TargetLook = Target.Rotation;
			if ( Target.Physics == PHYS_Walking )
				TargetLook.Pitch = 0;
			bClean = ( ((Vector(TargetLook) Dot Normal(Target.Velocity)) >= 0.71) && FastTrace(FireSpot, ProjStart) );
		}
		else // make sure that bot isn't leading into a wall
			bClean = FastTrace(FireSpot, ProjStart);
		if ( !bClean)
		{
			// reduce amount of leading
			if ( FRand() < 0.3 )
				FireSpot = Target.Location;
			else
				FireSpot = 0.5 * (FireSpot + Target.Location);
		}
	}

	bClean = false; //so will fail first check unless shooting at feet
	if ( FiredAmmunition.bTrySplash && (Pawn(Target) != None) && ((Skill >=4) || bDefendMelee)
		&& (((Target.Physics == PHYS_Falling) && (Pawn.Location.Z + 80 >= Target.Location.Z))
			|| ((Pawn.Location.Z + 19 >= Target.Location.Z) && (bDefendMelee || (skill > 6.5 * FRand() - 0.5)))) )
	{
	 	HitActor = Trace(HitLocation, HitNormal, FireSpot - vect(0,0,1) * (Target.CollisionHeight + 6), FireSpot, false);
 		bClean = (HitActor == None);
		if ( !bClean )
		{
			FireSpot = HitLocation + vect(0,0,3);
			bClean = FastTrace(FireSpot, ProjStart);
		}
		else
			bClean = ( (Target.Physics == PHYS_Falling) && FastTrace(FireSpot, ProjStart) );
	}
	if ( Pawn.Weapon != None && Pawn.Weapon.bSniping && Stopped() && (Skill > 5 + 6 * FRand()) )
	{
		// try head
 		FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
 		bClean = FastTrace(FireSpot, ProjStart);
	}

	if ( !bClean )
	{
		//try middle
		FireSpot.Z = Target.Location.Z;
 		bClean = FastTrace(FireSpot, ProjStart);
	}
	if ( FiredAmmunition.bTossed && !bClean && bEnemyInfoValid )
	{
		FireSpot = LastSeenPos;
	 	HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
		if ( HitActor != None )
		{
			bCanFire = false;
			FireSpot += 2 * Target.CollisionHeight * HitNormal;
		}
		bClean = true;
	}

	if( !bClean )
	{
		// try head
 		FireSpot.Z = Target.Location.Z + 0.9 * Target.CollisionHeight;
 		bClean = FastTrace(FireSpot, ProjStart);
	}
	if ( !bClean && (Target == Enemy) && bEnemyInfoValid )
	{
		FireSpot = LastSeenPos;
		if ( Pawn.Location.Z >= LastSeenPos.Z )
			FireSpot.Z -= 0.4 * Enemy.CollisionHeight;
	 	HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
		if ( HitActor != None )
		{
			FireSpot = LastSeenPos + 2 * Enemy.CollisionHeight * HitNormal;
			if ( Pawn.Weapon != None && Pawn.Weapon.SplashDamage() && (Skill >= 4) )
			{
			 	HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
				if ( HitActor != None )
					FireSpot += 2 * Enemy.CollisionHeight * HitNormal;
			}
			if ( Pawn.Weapon != None && Pawn.Weapon.RefireRate() < 0.99 )
				bCanFire = false;
		}
	}

	// adjust for toss distance
	if ( FiredAmmunition.bTossed )
		FireDir = AdjustToss(projspeed,ProjStart,FireSpot,true);
	else
		FireDir = FireSpot - ProjStart;

	FireRotation = Rotator(FireDir);
	realYaw = FireRotation.Yaw;

	FireRotation.Yaw = SetFireYaw(FireRotation.Yaw + aimerror);
	FireDir = vector(FireRotation);
	// avoid shooting into wall
	FireDist = FMin(VSize(FireSpot-ProjStart), 400);
	FireSpot = ProjStart + FireDist * FireDir;
	HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
	if ( HitActor != None )
	{
		if ( HitNormal.Z < 0.7 )
		{
			FireRotation.Yaw = SetFireYaw(realYaw - aimerror);
			FireDir = vector(FireRotation);
			FireSpot = ProjStart + FireDist * FireDir;
			HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
		}
		if ( HitActor != None )
		{
			FireSpot += HitNormal * 2 * Target.CollisionHeight;
			if ( Skill >= 4 )
			{
				HitActor = Trace(HitLocation, HitNormal, FireSpot, ProjStart, false);
				if ( HitActor != None )
					FireSpot += Target.CollisionHeight * HitNormal;
			}
			FireDir = Normal(FireSpot - ProjStart);
			FireRotation = rotator(FireDir);
		}
	}
	InstantWarnTarget(Target,FiredAmmunition,vector(FireRotation));
	ShotTarget = Pawn(Target);

	SetRotation(FireRotation);
	UpdatePawnViewPitch();
	return FireRotation;
}

//Is this function meant to be called for instant hit projectiles?
function InstantWarnTarget(Actor Target, FireProperties FiredAmmunition, vector FireDir)
{
	super.InstantWarnTarget(Target, FiredAmmunition, FireDir);
    //log("In RemoteBot: InstantWarnTarget");
}

//Here we send PRJ message
function ReceiveProjectileWarning(Projectile Proj)
{
	local Controller C;
	local Pawn Shooter;
	local vector FireDir;
	local vector Q, HelpVec;
	local vector aFacing,aToB;
	local float enemyDistance, orientation;

	Shooter = Proj.Instigator;

	if (Proj.IsA('FlakChunk'))
	{
		FireDir = Normal(Proj.Velocity);
	}
	else
	{
		FireDir = vector(Proj.Rotation);
	}

	// Some Controll
	if ( (Pawn == None) || (Pawn.health <= 0) )
		return;

	if ( CanSee(Shooter))
	{
        HelpVec = Shooter.Location + FireDir * 500;

		//These lines were taken from UnrealWiki
        //This counts the nearest point to our bot on the line created by origin of the projectile and his direction
		Q = shooter.Location + Normal( HelpVec - Shooter.Location ) * ((( HelpVec - Shooter.Location )
		dot ( Pawn.Location - Shooter.Location )) / VSize( Shooter.Location - HelpVec ));

		//Here we find out if Q is in front of shooter or behind him
		aFacing=Normal(Vector(shooter.Rotation));
 		aToB=Q - shooter.Location;
		orientation = aFacing dot aToB;

		if (LineOfSightTo(Proj) && inFront(Proj.Location))
		{
			enemyDistance = VSize(Pawn.Location - shooter.Location);

			myConnection.SendLine("PRJ {Time " $ (enemyDistance/Proj.Speed) $
				"} {Direction " $ FireDir $
				"} {Speed " $ Proj.Speed $
				"} {Velocity " $ Proj.Velocity $
				"} {Location " $ Proj.Location $
				"} {Origin " $ Shooter.Location $
				"} {DamageRadius " $ Proj.DamageRadius $
				"} {Type " $ Proj.Class $
				"}");
		}


		//If the projectile flies at distance 100 or less from our bot we send the message
		/*if ((orientation > 0) && (VSize( Pawn.Location - Q ) < 100))
		{
			enemyDistance = VSize(Pawn.Location - shooter.Location);

			myConnection.SendLine("PRJ" $ib$as$ "Time" $ib$ (enemyDistance/Proj.Speed) $ae$ib$as$
				"Direction" $ib$ FireDir $ae$ib$as$
				"Origin" $ib$ Shooter.Location $ae$ib$as$
				"DamageRadius" $ib$ Proj.DamageRadius $ae$ib$as$
				"Class" $ib$ Proj.Class $ae);
		}  */
	}

}


// Projectiles are handled elsewhere
event ReceiveWarning(Pawn shooter, float projSpeed, vector FireDir)
{
	log("We ve been in ReceiveWarning");

}

function bool TryToDuck(vector duckDir, bool bReversed);

// CloseToPointMan - called if orders are 'follow' to check if close enough to point man
function bool CloseToPointMan(Pawn Other)
{
	//return what engine wants to hear
	return true;
}

//Don't need our bots autotaunting
function MaybeTaunt(Pawn Other);

//Called when someone other than this bot dies.
//TODO: We dont need this
function Killed(pawn Killer, pawn Other, name damageType);

//Pointless callback
function EnemyAcquired();

//All kinds of shit can call this mostly special trigger points
function Trigger( actor Other, pawn EventInstigator )
{
	myConnection.SendLine("TRG" $ib$as$ "Actor" $ib$ Other $ae$ib$as$
		"EventInstigator" $ib$ EventInstigator.Controller $ EventInstigator.Controller.PlayerReplicationInfo.PlayerId $ae);
}

//Much of translocator brains implemented in translocator
//For now better off playing without it - need to research how
//it interacts with path finding
function TranslocateToTarget(Actor Destn)
{
	//MyTranslocator.DesiredTarget = Destn;
}

//Don't let engine pick nodes that must be impact jumped
function bool CanImpactJump()
{
	return false;
}

//Don't handle impact jumps or low gravity manuevers for bots
function ImpactJump();
function BigJump(Actor JumpDest);

//Don't have engine direct to Ambush
function bool FindAmbushSpot()
{
	return false;
}

//Called when bot is injured
function NotifyTakeHit
(
	pawn InstigatedBy,
	vector HitLocation,
	int Damage,
	class<DamageType> damageType,
	vector Momentum
)
{
	local float enemyDist;
	local vector enemyDir, X, Y, Z;
	local string messageString;

	Super.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);

	messageString = "DAM" $ib$as$ "Damage" $ib$ Damage $ae$ib$as$ "DamageType" $ib$ damageType $ae;

	//TODO: !!! need to fix this and projectile detection to match our
	//periphreal vision parameter.
	// only if tight FOV
	GetAxes(Rotation,X,Y,Z);
	enemyDist = VSize(instigatedBy.Location - Location);
	enemyDir = (instigatedBy.Location - Location)/enemyDist;
	if ((enemyDir Dot X) > 0.8)
		messageString = messageString $ib$as$
			"Instigator" $ib$ instigatedBy.Controller$instigatedBy.Controller.PlayerReplicationInfo.PlayerID $ae;

	myConnection.SendLine(messageString);

	//Here we notifyies RemoteBot about they made a hit.
	if( InstigatedBy.Controller != none && InstigatedBy.Controller.isA('RemoteBot'))
	{
	    RemoteBot(InstigatedBy.Controller).RemoteNotifyHit(self, Damage, DamageType );
    }
}

function RemoteNotifyHit(Controller Victim, int Damage, class<DamageType> damageType)
{
	myConnection.sendLine("HIT" $ib$as$ "Id" $ib$ Victim $
                 Victim.PlayerReplicationInfo.PlayerID $ae$ib$as$ "Damage" $ib$ Damage $ae$ib$as$
                 "DamageType" $ib$ DamageType $ae);
}


function SetFall()
{
	if (Pawn.bCanFly)
	{
		Pawn.SetPhysics(PHYS_Flying);
		return;
	}
	if (bDebug)
		log("In RemoteBot.uc: SetFall() enganged.");
	/*
	if ( Pawn.bNoJumpAdjust )
	{
		Pawn.bNoJumpAdjust = false;
		return;
	}
	else
	{
		bPlannedJump = true;
		Pawn.Velocity = EAdjustJump(Pawn.Velocity.Z,Pawn.GroundSpeed);
		Pawn.Acceleration = vect(0,0,0);
	} */
}


//**********************************************************************************
//Base Monster AI

/*
auto state StartUp
{
	function BeginState()
	{
		SetMovementPhysics();
		if (Physics == PHYS_Walking)
			SetPhysics(PHYS_Falling);
	}

Begin:
}
*/

auto state StartUp
{
	function BeginState()
	{
		//log("StartUp,BeginState");
		//TODO:HACK: Should examine Squad issues if it should be here

		if (Squad == none)
			Squad = spawn(class'BotAPI.GBSquadAI');

		Squad.AddBot(self);

	}

	function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
						vector momentum, class<DamageType> damageType)
	{
		Global.TakeDamage(Damage, instigatedBy, hitlocation, momentum, damageType);
	}
	function Landed(vector HitNormal)
	{
		Global.Landed(HitNormal);
	}


Begin:
    //This is necessary, because if someone keeps shooting on our bot,
	//sometimes it starts sliding to side
	//log("Physics "$Physics$"Pawn Physics "$Pawn.Physics);
	if (Pawn != none)
	{
		if (Pawn.Physics != PHYS_Falling)
		{
			Pawn.Velocity = vect(0,0,0);
			Pawn.Acceleration = vect(0,0,0);
			MoveTimer = -1.0; //TODO: Should check what the hell is this
		}
	}
 	sleep(0.5);
	goto 'Begin';

DoStop:
	if (Pawn != none)
	{
		if (Pawn.Physics == PHYS_Falling) //TODO: This is just a test, but it should repair one issue.
		{
			WaitForLanding(); //We wait when we are on ground and THEN we will stop.
			Pawn.Velocity = vect(0,0,0);
			Pawn.Acceleration = vect(0,0,0);
			MoveTimer = -1.0;
		}
		else
		{
			Pawn.Velocity = vect(0,0,0);
			Pawn.Acceleration = vect(0,0,0);
			MoveTimer = -1.0; //TODO: Should check what the hell is this
		}
	}
	//Pawn.PlayWaiting();
	goto 'Begin';
Test:
	log("Startup, Test");

	//This should handle setting right focus, when we are on the lift
	if (Pawn.Velocity.z != 0) //We are moving in z direction
	{
		FocalPoint.z = Pawn.Location.z; //Update z coordinate of FocalPoint variable
	}

	sleep( 0.1 );
	goto 'Test';
MoveToPoint:
	if( pawn != none ) {
		//log("Focus before move "$Focus$"FocalPoint before move "$FocalPoint);
		//log("Rotation before move "$Pawn.Rotation);

		MoveTo(myDestination, , Pawn.bIsWalking);
	}
	//log("Rotation after move "$Pawn.Rotation);
	//log("Focus after move "$Focus$"FocalPoint after move "$FocalPoint);
	FocalPoint = myFocalPoint;
	//FinishRotation(); //dunno if this should be here or not
	goto 'Begin';
MoveToActor:
	if( pawn != none ) {

		MoveToward(MoveTarget, , , , Pawn.bIsWalking);
		//myConnection.SendLine("FINM"); //just a test abou message completion
	}
	FocalPoint = myFocalPoint;
	goto 'Begin';
Strafe:
	if (Pawn != None)
	{
		MoveTo( Destination, Focus , Pawn.bIsWalking );
	}
	FocalPoint = myFocalPoint;
	goto 'Begin';
MoveAlong: //Moves swiftly between two locations - could be a problem normaly because  of TCP/IP delays
	if (Pawn != None)
	{
		MoveTo( myDestination, , Pawn.bIsWalking );
		MoveTo( pendingDestination, , Pawn.bIsWalking);

		FocalPoint = Pawn.Location + 300 * vector(Pawn.Rotation);

	}
	goto 'Begin';
MoveContinuous:
	if (Pawn != None)
	{
		MoveTo( Pawn.Location + 300 * vector(Pawn.Rotation), , Pawn.bIsWalking );
		FocalPoint = Pawn.Location + 300 * vector(Pawn.Rotation);
		goto 'MoveContinuous';
	}
	goto 'Begin';
Turning:
//This substate probably not needed - not used right now
	//TODO: Is this needed here?
	if (Pawn.Physics != PHYS_Falling)
	{
		Velocity = vect(0,0,0);
		Acceleration = vect(0,0,0);
		MoveTimer = -1.0;
	}

	//log("Pawn  view pitch before "$Pawn.ViewPitch);

	UpdatePawnViewPitch();
	FinishRotation();

	goto 'Begin';
}

// This state was called somehow on our bot. That is highly undersirable.
// Overriding
state Roaming
{
	function BeginState()
	{
		//log("In Roaming STATE! Shouldnt be!");
		gotostate('StartUp','Begin');
	}
Begin:
	gotostate('StartUp','Begin');
}

state TakeHit
{
	//ignores seeplayer, hearnoise, bump, hitwall;

	function Timer();

Begin:
	error("!!!TakeHit");
}

state GameEnded
{
ignores SeePlayer, EnemyNotVisible, HearNoise, TakeDamage, Bump, Trigger, HitWall, ZoneChange, Falling, ReceiveWarning;

	function SpecialFire()
	{
	}
	function bool TryToDuck(vector duckDir, bool bReversed)
	{
		return false;
	}
	function SetFall()
	{
	}
	function LongFall()
	{
	}
	function Killed(pawn Killer, pawn Other, name damageType)
	{
	}
	function ClientDying(class<DamageType> DamageType, vector HitLocation)
	{
	}

	function BeginState()
	{
		Pawn.SimAnim.AnimRate = 0.0;
		bFire = 0;
		bAltFire = 0;

		SetCollision(false,false,false);
		SetPhysics(PHYS_None);
		Velocity = vect(0,0,0);
		myConnection.SendLine("FIN");
	}
}

state Dead
{
ignores SeePlayer, HearNoise, KilledBy;

function BeginState()
{
	//log("State: Dead, fc BeginState()");
	//We can be sent to this state sometimes, when it is not desired ( by game mechanics)
	//Escaping here
	if (Pawn != none)
		gotostate('StartUp','Begin');

	//This is taken from working AI in UT2004, probably needed to assure bot
	//will behave normally after restart - 02/03/07 Michal Bida
	Enemy = None;
	Focus = None;
	Target = None;
	RouteGoal = None;
	MoveTarget = None;
	bFire = 0;
	bAltFire = 0;
	Super.StopFiring(); //Not needed anymore to call super, but for sure.
	FormerVehicle = None;
	bFrustrated = false;
	BlockedPath = None;
	bInitLifeMessage = false;
	bPlannedJump = false;
	bInDodgeMove = false;
	bReachedGatherPoint = false;
	bFinalStretch = false;
	bWasNearObjective = false;
	bPreparingMove = false;
	bEnemyEngaged = false;
	bPursuingFlag = false;


}
/*
function EndState()
{
	myConnection.sendLine("SPW2");
}*/
Begin:
	log("In Dead:Begin:");
	//AutoSpawn policy
	if (Pawn != None)
		gotostate('StartUp','Begin');

	sleep(0.05);
	if (bAutoSpawn)// && !Level.Game.bWaitingToStartMatch)
	{
		RemoteRestartPlayer();
	//	if (Pawn == none) //bug during restart?
	//		goto('Begin');
		//ServerRestartPlayer(); //Dunno if this is needed here - Could it cause troubles?
	}
	if (Pawn != None)
		gotostate('StartUp','Begin');

/*	if (!Level.Game.bWaitingToStartMatch)
	{
		RemoteRestartPlayer();
	}
	if (Pawn != None)
		gotostate('StartUp','Begin');*/
	sleep(1.0);
	goto('Begin');

}



//-------------RemoteBot Specific Functions--------------------


//Check to see if a location is really in front of the bot
//may be a more efficient algorithm
function bool inFront(Vector A)
{
	local rotator angle;

	//can't be infront if inside bot
	if( VSize(Pawn.Location - A) < CollisionRadius )
		return false;

	angle = ( Pawn.Rotation - rotator(A - Pawn.Location) );

	if( (cos(angle.Yaw / 10435.0) < remoteVisionLimit) ||
		(cos(angle.Pitch / 10435.0) < remoteVisionLimit) )
		return false;
	else
		return true;
}


//Called by the gametype when someone else is injured by the bot
//TODO: From old gamebots, is not called anymore, TO REMOVE - mb
/*
function int HurtOther(int Damage, name DamageType, pawn injured)
{
	myConnection.SendLine("HIT" $ib$as$ "Id" $ib$ injured $ae$ib$as$
		"Damage" $ib$ Damage $ae$ib$as$
		"DamageType" $ib$ DamageType $ae);
} */

function checkSelf()
{
	local string outstring, TeamIndex, altFiring;
	local bool bIsShooting;

	if(Level.Game.isA('BotTeamGame'))
	{
		TeamIndex = string(PlayerReplicationInfo.Team.TeamIndex);
	}
	else
	{
    	TeamIndex = "255";
	}


	if( Pawn != none )
	{
		if( Pawn.Weapon != None)
		    bIsShooting = Pawn.Weapon.IsFiring();

		outstring = "SLF {Id " $ self $ PlayerReplicationInfo.PlayerID $
			"} {Rotation " $ (int(Pawn.ViewPitch) * 65556/255)$","$Pawn.Rotation.Yaw$",0" $
			"} {Location " $ Pawn.Location $
			"} {Velocity " $ Pawn.Velocity $
			"} {Name " $ PlayerReplicationInfo.PlayerName $
			"} {Team " $ TeamIndex $
			"} {Health " $ Pawn.Health $
			"} {Weapon " $ Pawn.Weapon $
			"} {Shooting " $ bIsShooting $
			"} {Armor " $ int(Pawn.ShieldStrength) $
			"} {Adrenaline " $ Adrenaline $
			"}";

		if( Pawn.Weapon != None)
		{
			outstring = outstring $ " {CurrentAmmo " $ Pawn.Weapon.AmmoAmount(0) $
				"} {CurrentAltAmmo " $ Pawn.Weapon.AmmoAmount(1) $"}";

			if( Pawn.Weapon.GetFireMode(1).IsFiring() )
				altFiring = "1";
			else
				altFiring = "0";

			outstring = outstring $ " {AltFiring " $ altFiring $"}";
		}

        //log("Debug: bFire "$bFire$" bAltFire "$bAltFire);

		myConnection.sendLine(outstring);
	}
	else
	{
		log("Pawn is none in CheckSelf() ");
	}


}

function checkVision()
{
	local Pickup Pickup; //Respawned Inventory on the map is now Pickup;
	local Mover M;
	local Controller C;
	local CTFFlag F;
	local NavigationPoint N;
//	local vector HitL, HitN;
	local int temp;
	local string outstring, TeamIndex, WeaponClass, item, itemClass, flag, uniqueId;
	local bool flagVisible;

	if( Pawn == none )
	{
		log("In CheckVision() - Pawn is none ");
		return;
	}

	//!!! view rotation sometimes falls out of synch with rotation? wtf?

	for ( C=Level.ControllerList; C!=None; C=C.NextController )
	{
		if( C != self && C.Pawn != none && CanSee(C.Pawn))//to match peripheral vision
		{

			if(Level.Game.isA('BotTeamGame'))
			{
				TeamIndex = string(C.PlayerReplicationInfo.Team.TeamIndex);
			}
			else
			{
    			TeamIndex = "255";
			}
			if (C.Pawn.Weapon != none)
			{
				WeaponClass = string(C.Pawn.Weapon.Class);
			}
			else
			{
				WeaponClass = "None";
			}
			outstring = "PLR {Id " $ C $ C.PlayerReplicationInfo.PlayerID $
			"} {Rotation " $ C.Pawn.Rotation $
			"} {Location " $ C.Pawn.Location $
			"} {Velocity " $ C.Pawn.Velocity $
			"} {Name " $ C.PlayerReplicationInfo.PlayerName $
			"} {Team " $ TeamIndex $
			"} {Reachable " $ actorReachable(C.Pawn) $
			"} {Weapon " $ WeaponClass $
			"}";

			if( C.Pawn.Weapon.GetFireMode(0).IsFiring() )
				outstring = outstring $" {Firing 1}";
			else if( C.Pawn.Weapon.GetFireMode(1).IsFiring() )
				outstring = outstring $" {Firing 2}";
			else
				outstring = outstring $" {Firing 0}";

			myConnection.sendLine(outstring);

        }//end if
	}//end for P=Level.ControllerList
	/*
	if (bDebug)
	{
		log("Testing allactors pickup");
		//StopWatch(false);
	}*/
	foreach AllActors(class'Pickup',Pickup)
	{
		if( (Pickup.GetStateName() == 'Pickup') && !Pickup.bHidden && inFront(Pickup.Location) && LineOfSightTo(Pickup) )
		{
			// The items, which have pickup base DOES NOT have unique id.
			// We need to send unique id, so sending id of pickup base.
			if (Pickup.PickUpBase == none)
				uniqueId = string(Pickup);
			else
				uniqueId = string(Pickup.PickUpBase);

			myConnection.SendLine("INV {Id " $ uniqueId $
				"} {Event See" $
				"} {Amount " $ myConnection.GetItemAmount(Pickup) $
				"} {Location " $ Pickup.Location $
				"} {Reachable " $ actorReachable(Pickup) $
				"} {Class " $ Pickup.Class $"}");

				/*$ib$as$
				"Base" $ib$ Pickup.PickUpBase $ae);
				  */
			//if (InStr(pickup.Name,'MiniHealth')!= -1)
				//log("Sending this Vial: "$pickup.Name);
		}
	}/*
	if (bDebug)
	{
		//StopWatch(true);
	}*/

	foreach AllActors(class'Mover',M)
	{
		if( !M.bHidden && inFront(M.Location) && LineOfSightTo(M) )//&& Pawn.FastTrace(M.Location) )
		{
			outstring = "MOV {Id " $ M $
				"} {Location " $ M.Location $
				"} {Reachable " $ actorReachable(M) $ //not sure if this works for lifts(bots may be forced to wait for lift)
				"} {DamageTrig " $ M.bDamageTriggered $
				"} {Class " $ M.Class $
				"} {IsMoving " $ M.bInterpolating $
				"} {Velocity " $ M.Velocity $
				"}";

			myConnection.SendLine(outstring);
		}
	}
	if (bDebug)
	{
		log("Testing navpoints: ");
		//Stopwatch(false);
	}
	for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
	{
		//Added attribute Visible, so inFront is not here anymore
		if( LineOfSightTo(N))//FastTrace(N.Location) )
		{
			//ControlPoint is now xDomPoint
			if(N.IsA('xDomPoint'))
			{
				if( xDomPoint(N).ControllingTeam == none )
					temp = 255;
				else
					temp = xDomPoint(N).ControllingTeam.TeamIndex;

				myConnection.SendLine("DOM {Id " $ N $
					"} {Location " $ N.Location $
					"} {Visible " $ inFront( N.Location ) $
					"} {Reachable " $ actorReachable(N) $
					"} {Controller " $ temp $"}");
			}
			else
			{
				flag = "PathNode";
				item = "None";
				itemClass = "None";
				if (N.IsA('InventorySpot'))
				{
					flag = "InventorySpot";

					// The items, which have pickup base DOES NOT have unique id.
					// We need to send unique id, so sending id of pickup base.
					if (InventorySpot(N).markedItem.PickUpBase == none)
						item = string(InventorySpot(N).markedItem);
					else
						item = string(InventorySpot(N).markedItem.PickUpBase);
					itemClass = string(InventorySpot(N).markedItem.class);
				}

				if (N.IsA('PlayerStart'))
				{
					flag = "PlayerStart";
				}


				if (N.IsA('AIMarker') && AIMarker(N).markedScript.IsA('UnrealScriptedSequence'))
				{

					flag = "AIMarker";

					myConnection.SendLine("NAV {Id " $ N $
						"} {Location " $ N.Location $
						"} {Visible " $ inFront(N.Location) $
						"} {Item " $ item $
						"} {ItemClass " $ itemClass $
						"} {Flag " $ flag $
						"} {Reachable " $ actorReachable(N) $
						"} {Rotation " $ UnrealScriptedSequence(AIMarker(N).markedScript).Rotation $
						"} {RoamingSpot " $ UnrealScriptedSequence(AIMarker(N).markedScript).bRoamingScript $
						"} {SnipingSpot " $ UnrealScriptedSequence(AIMarker(N).markedScript).bSniping $
						"} {PreferedWeapon " $ UnrealScriptedSequence(AIMarker(N).markedScript).WeaponPreference $
						"}");

				}
				else
				{
					myConnection.SendLine("NAV {Id " $ N $
						"} {Location " $ N.Location $
						"} {Visible " $ inFront(N.Location) $
						"} {Item " $ item $
						"} {ItemClass " $ itemClass $
						"} {Flag " $ flag $
						"} {Reachable " $ actorReachable(N) $
						"}");
				}
			}
		}
	}//end for N=Level.NavigationPointList
	if (bDebug)
	{
		//StopWatch(true);
	}


	if (BotDeathMatch(Level.Game).GameClass == "BotCTFGame")
	{
		foreach AllActors (class'CTFFlag', F)
		{
			flagVisible = false;

			outstring = "FLG {Id " $ F $
				"} {Team " $ F.Team.TeamIndex $
				"} {Reachable " $ actorReachable(F) $
				"} {State " $ F.GetStateName() $
				"}";

			//when a flag is held its location is not updated by engine =(
			if(F.IsInState('Held') && F.Holder != none)
			{
				if ( LineOfSightTo(F.Holder) && inFront(F.Holder.Location) )
				{
					outstring = outstring $ " {Location " $ F.Holder.Location $
						"} {Holder " $ F.Holder.Owner $ Controller(F.Holder.Owner).PlayerReplicationInfo.PlayerID $"}";
					flagVisible = True;
				}
				else if (F.Holder == Pawn)
				{
					outstring = outstring $ " {Holder " $ F.Holder.Owner $ Controller(F.Holder.Owner).PlayerReplicationInfo.PlayerID $"}";
				}
			}
			else
			{
				if (LineOfSightTo(F) && inFront(F.Location))
				{
					outstring = outstring $ " {Location " $ F.Location $"}";
					flagVisible = True;
				}
			}

			outstring = outstring $ " {Visible " $ flagVisible $"}";

			myConnection.SendLine(outstring);

		}
	}
}

//Shortten up the MakeItem calls
function string MakeItem(string first, string second)
{
	return myConnection.MakeItem(first,second);
}

simulated event Destroyed()
{
	local LinkedReplicationInfo PRI;

    //Destroying all linked replication infos
	for (PRI = PlayerReplicationInfo.CustomReplicationInfo; PRI != none; PRI = PRI.NextReplicationInfo)
	{
		PRI.Destroy();
	}


	if(Pawn != None)
	{
		Pawn.Destroy();
		Pawn = None;
    }

    if (myTarget != none)
    {
		myTarget.Destroy();
	}

	if (myConnection != none)
		myConnection.SendLine("FIN");
	else
		log("Problem with sending FIN, myConnection = none");

    Super.Destroyed();
}

function ChangeWeapon();
state MoveToGoal
{
	function BeginState();
}

function ServerChangedWeapon(Weapon OldWeapon, Weapon NewWeapon);

function Tick(float DeltaTime)
{
	super.Tick(DeltaTime);
	if (bShowFocalPoint)
	{
		myConnection.FocusActor.bHidden = false;
		if (Focus != none)
		{
		    myConnection.FocusActor.SetLocation(Focus.Location);
		}
		else
		{
			myConnection.FocusActor.SetLocation(FocalPoint);
		}
	}
	else
		myConnection.FocusActor.bHidden = true;
	//log("In RemoteBot: Tick()");
}

//-----------------

defaultproperties
{
     remoteVisionLimit=0.707000
     bAutoSpawn=True
     bDrawTraceLines=True
     MaxSpeed=2.000000
     DesiredSkin="ThunderCrash.JacobM"
}
